package com.seewo.dubbo.debugger.core;

import com.alibaba.fastjson.JSON;
import com.seewo.dubbo.debugger.api.Arg;
import com.seewo.dubbo.debugger.api.Request;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.MethodInvoker;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * api接口调试器
 *
 * @author chenliangliang
 * @date 2018/7/26
 */
@Slf4j
class ApiInvoker {

    private static ConcurrentHashMap<String, Class<?>> classMap = new ConcurrentHashMap<>(128);
    private static ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(32);


    Object invoke(Request request) throws Exception {
        checkRequest(request);
        ApplicationContext ctx = ApiContext.getApplicationContext();
        final String interfaceName = request.getInterfaceName();
        Object beanObj = beanMap.get(interfaceName);
        if (beanObj == null) {
            Class<?> aClass = classMap.get(interfaceName);
            if (aClass == null) {
                aClass = loadClass(interfaceName);
                classMap.put(interfaceName, aClass);
            }
            beanObj = ctx.getBean(aClass);
            beanMap.put(interfaceName, beanObj);
        }
        MethodInvoker methodInvoker = new MethodInvoker();
        methodInvoker.setTargetClass(beanObj.getClass());
        methodInvoker.setTargetObject(beanObj);
        methodInvoker.setTargetMethod(request.getMethodName());
        List<Arg> argList = request.getArgs();
        int size = argList.size();
        if (size > 0) {
            Object[] objects = new Object[size];
            argList.forEach(arg -> objects[arg.getIndex()] = arg2Obj(arg));
            methodInvoker.setArguments(objects);
        }
        methodInvoker.prepare();

        return methodInvoker.invoke();
    }

    private void checkRequest(Request request) {
        Assert.notNull(request, "request不能为空");
        Assert.notNull(request.getInterfaceName(), "interfaceName不能为空");
        Assert.notNull(request.getMethodName(), "methodName不能为空");
    }

    private Object arg2Obj(Arg arg) {
        final String type = arg.getType();
        final String value = arg.getValue();
        final String className = arg.getClassName();
        Object obj;
        switch (type) {
            case "integer":
                obj = Integer.valueOf(value);
                break;
            case "long":
                obj = Long.valueOf(value);
                break;
            case "string":
                obj = String.valueOf(value);
                break;
            case "boolean":
                obj = Boolean.valueOf(value);
                break;
            case "float":
                obj = Float.valueOf(value);
                break;
            case "double":
                obj = Double.valueOf(value);
                break;
            case "array":
                Class<?> clz = classMap.get(className);
                if (clz == null) {
                    clz = loadClass(className);
                    classMap.put(className, clz);
                }
                List<?> list = JSON.parseArray(value, clz);
                int size = list.size();
                obj = list.toArray(new Object[size]);
                break;
            case "object":
                Class<?> clazz = classMap.get(className);
                if (clazz == null) {
                    clazz = loadClass(className);
                    classMap.put(className, clazz);
                }
                obj = JSON.parseObject(value, clazz);
                break;
            default:
                obj = null;
                break;
        }
        return obj;
    }


    private Class<?> loadClass(String className) {
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.warn("加载类{}失败", className, e);
        }
        return clazz;
    }
}
